package com.clp.protocol.iec104.client.pipeline.state;

import com.clp.protocol.iec104.apdu.IAsduFactory;
import com.clp.protocol.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.client.InMaster;
import com.clp.protocol.iec104.client.MasterIAsduSender;
import com.clp.protocol.iec104.client.async.MasterFuture;
import com.clp.protocol.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.client.async.sendapdu.*;
import com.clp.protocol.iec104.client.pipeline.MPipelineManager;
import com.clp.protocol.iec104.definition.Tc;
import com.clp.protocol.iec104.definition.quatype.TaQuaType;
import com.clp.protocol.core.pdu.nbytepdu.FailedToSendFrameException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;

public class MInternalIAsduSender implements MasterIAsduSender {
    private final MPipelineManager pipelineManager;
    private final MAsduSendEntrance entrance;

    public MInternalIAsduSender(MPipelineManager pipelineManager, MAsduSendEntrance entrance) {
        this.pipelineManager = pipelineManager;
        this.entrance = entrance;
    }

    @Override
    public InMaster master() {
        return pipelineManager.getInMaster();
    }

    /********************************************* chSend ******************************************************/

    public ChannelFuture chSend(IAsdu iAsdu) {
        ChannelPromise promise = newChannelPromise();
        safeExecute(() -> entrance.tryChannelSend(iAsdu, promise));
        return promise;
    }

    private ChannelPromise newChannelPromise() {
        return master().channel().newPromise();
    }

    private void safeExecute(Runnable task) {
        master().executor().execute(task);
    }

    private int localRtuAddr() {
        return master().localRtuAddr();
    }

    /************************************************* send ****************************************************/

    @Override
    public MasterFuture<SendTotalCall100Res> sendTotalCall100() {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTotalCall100(master.localRtuAddr());
        MasterPromise<SendTotalCall100Res> sendPromise = master.newPromise(new SendTotalCall100Res());
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTotalCall100()) {
            sendPromise.getRes().setSendSuccess(false).setRecvTotalCall100Ack(false).setRecvTotalCall100End(false);
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
            return sendPromise;
        }
        // 将 这个Promise注册到对应的状态中
        pipelineManager.getMTotalCall100DataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTotalCall101Res> sendTotalCall101() {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTotalCall101(master.localRtuAddr());
        MasterPromise<SendTotalCall101Res> sendPromise = master().newPromise(new SendTotalCall101Res());
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTotalCall101()) {
            sendPromise.getRes().setSendSuccess(false).setRecvTotalCall101Ack(false).setRecvTotalCall101End(false);
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
            return sendPromise;
        }
        // 将 这个Promise注册到对应的状态中
        pipelineManager.getMTotalCall101DataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcSelectRes> sendOnePointTcSelect(int infoObjAddr, boolean isSwitchOn) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfOnePointTc(master.localRtuAddr(), infoObjAddr, Tc.CmdType.SELECT,
                isSwitchOn ? Tc.OnePointSwitch.ON : Tc.OnePointSwitch.OFF);
        MasterPromise<SendTcSelectRes> sendPromise = new SendTcSelectMasterPromise(master, new SendTcSelectRes(), master.localRtuAddr(),
                infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTcSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false)
                    .setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
            return sendPromise;
        }
        // 注册
        pipelineManager.getMTcDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcSelectRes> sendTwoPointTcSelect(int infoObjAddr, boolean isSwitchOn) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTwoPointTc(master.localRtuAddr(), infoObjAddr, Tc.CmdType.SELECT,
                isSwitchOn ? Tc.TwoPointSwitch.ON : Tc.TwoPointSwitch.OFF);
        MasterPromise<SendTcSelectRes> sendPromise = new SendTcSelectMasterPromise(master, new SendTcSelectRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTcSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false)
                    .setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
            return sendPromise;
        }
        // 注册
        pipelineManager.getMTcDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcExecuteRes> sendOnePointTcExecute(int infoObjAddr, boolean isSwitchOn) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfOnePointTc(master.localRtuAddr(), infoObjAddr, Tc.CmdType.EXECUTE,
                isSwitchOn ? Tc.OnePointSwitch.ON : Tc.OnePointSwitch.OFF);
        MasterPromise<SendTcExecuteRes> sendPromise = new SendTcExecuteMasterPromise(master, new SendTcExecuteRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTcExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false)
                    .setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
            return sendPromise;
        }
        // 注册
        pipelineManager.getMTcDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcExecuteRes> sendTwoPointTcExecute(int infoObjAddr, boolean isSwitchOn) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTwoPointTc(master.localRtuAddr(), infoObjAddr, Tc.CmdType.EXECUTE,
                isSwitchOn ? Tc.TwoPointSwitch.ON : Tc.TwoPointSwitch.OFF);
        MasterPromise<SendTcExecuteRes> sendPromise = new SendTcExecuteMasterPromise(master, new SendTcExecuteRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTcExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false)
                    .setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
            return sendPromise;
        }
        // 注册
        pipelineManager.getMTcDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaSelectRes> sendTaSelectNormalized(int infoObjAddr, int setVal) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTaNormalized(master.localRtuAddr(), infoObjAddr, setVal, TaQuaType.SELECT);
        MasterPromise<SendTaSelectRes> sendPromise = new SendTaSelectMasterPromise(master, new SendTaSelectRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTaSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false)
                    .setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
        }
        // 注册
        pipelineManager.getMTaDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaExecuteRes> sendTaExecuteNormalized(int infoObjAddr, int setVal) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTaNormalized(master.localRtuAddr(), infoObjAddr, setVal, TaQuaType.EXECUTE);
        MasterPromise<SendTaExecuteRes> sendPromise = new SendTaExecuteMasterPromise(master, new SendTaExecuteRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTaExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false)
                    .setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
        }
        // 注册
        pipelineManager.getMTaDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaSelectRes> sendTaSelectFloat(int infoObjAddr, float setVal) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTaFloat(master.localRtuAddr(), infoObjAddr, setVal, TaQuaType.SELECT);
        MasterPromise<SendTaSelectRes> sendPromise = new SendTaSelectMasterPromise(master, new SendTaSelectRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTaSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false)
                    .setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
        }
        // 注册
        pipelineManager.getMTaDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaExecuteRes> sendTaExecuteFloat(int infoObjAddr, float setVal) {
        InMaster master = master();
        IAsdu iAsdu = IAsduFactory.getIAsduOfTaFloat(master.localRtuAddr(), infoObjAddr, setVal, TaQuaType.EXECUTE);
        MasterPromise<SendTaExecuteRes> sendPromise = new SendTaExecuteMasterPromise(master, new SendTaExecuteRes(),
                master.localRtuAddr(), infoObjAddr);
        if (!master.controlInfo().isInitCompleted() || master.controlInfo().isDoingTaExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false)
                    .setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iAsdu));
        }
        // 注册
        pipelineManager.getMTaDataHandler().register(sendPromise);
        chSend(iAsdu);
        return sendPromise;
    }
}
